package com.iam.core.utils;

import com.iam.core.config.Rsaconfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.rsa.crypto.RsaAlgorithm;
import org.springframework.stereotype.Component;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.*;
import java.util.Base64;

/**
 * @Author:zhao-baolin
 * @Description:
 * @Date:Created in 2018/7/4
 * @Modified By:
 */
@Component
public class RsaUtil {

    @Autowired
    private Rsaconfig rsAconfig;

    /**
     * 指定加密算法为DESede
     * MD5withRSA///RSA/ECB/PKCS1Padding
     */
    private static String ALGORITHM = "RSA";
    /**
     * 指定key的大小(64的整数倍,最小512位)
     */
    private static int KEYSIZE = 1024;
    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;
    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 128;
    /**
     * 公钥模量
     */
    public static String publicModulus = null;
    /**
     * 公钥指数
     */
    public static String publicExponent = null;
    /**
     * 私钥模量
     */
    public static String privateModulus = null;
    /**
     * 私钥指数
     */
    public static String privateExponent = null;
    private static KeyFactory keyFactory = null;

    static {
        try {
            keyFactory = KeyFactory.getInstance("RSA");
        } catch (NoSuchAlgorithmException ex) {
            System.out.println(ex.getMessage());
        }
    }

    public RsaUtil() {
        try {
            generateKeyPairString(KEYSIZE);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public RsaUtil(int keySize) {
        try {
            generateKeyPairString(keySize);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成密钥对字符串
     */
    private void generateKeyPairString(int keySize) throws Exception {
        /**
         * RSA算法要求有一个可信任的随机数源
         */
        SecureRandom sr = new SecureRandom();
        /** 为RSA算法创建一个KeyPairGenerator对象 */
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
        /** 利用上面的随机数据源初始化这个KeyPairGenerator对象 */
        kpg.initialize(keySize, sr);
        /** 生成密匙对 */
        KeyPair kp = kpg.generateKeyPair();
        /** 得到公钥 */
        Key publicKey = kp.getPublic();
        /** 得到私钥 */
        Key privateKey = kp.getPrivate();
        /** 用字符串将生成的密钥写入文件 获取算法 */
        String algorithm = publicKey.getAlgorithm();
        KeyFactory keyFact = KeyFactory.getInstance(algorithm);
        BigInteger prime = null;
        BigInteger exponent = null;

        RSAPublicKeySpec keySpec = (RSAPublicKeySpec) keyFact.getKeySpec(publicKey, RSAPublicKeySpec.class);
        prime = keySpec.getModulus();
        exponent = keySpec.getPublicExponent();
        RsaUtil.publicModulus = HexUtil.bytes2Hex(prime.toByteArray());
        RsaUtil.publicExponent = HexUtil.bytes2Hex(exponent.toByteArray());

        RSAPrivateCrtKeySpec privateKeySpec = (RSAPrivateCrtKeySpec) keyFact.getKeySpec(privateKey, RSAPrivateCrtKeySpec.class);
        BigInteger privateModulus = privateKeySpec.getModulus();
        BigInteger privateExponent = privateKeySpec.getPrivateExponent();
        RsaUtil.privateModulus = HexUtil.bytes2Hex(privateModulus.toByteArray());
        RsaUtil.privateExponent = HexUtil.bytes2Hex(privateExponent.toByteArray());
    }

    /**
     * 根据给定的16进制系数和专用指数字符串构造一个RSA专用的公钥对象。
     *
     * @param hexModulus        系数。
     * @param hexPublicExponent 专用指数。
     * @return RSA专用公钥对象。
     */
    public static RSAPublicKey getRsaPublicKey(String hexModulus, String hexPublicExponent) {
        if (isBlank(hexModulus) || isBlank(hexPublicExponent)) {
            System.out.println("hexModulus and hexPublicExponent cannot be empty. return null(RSAPublicKey).");
            return null;
        }
        byte[] modulus = null;
        byte[] publicExponent = null;
        try {
            modulus = HexUtil.hex2Bytes(hexModulus);
            publicExponent = HexUtil.hex2Bytes(hexPublicExponent);
        } catch (Exception ex) {
            System.out.println("hexModulus or hexPublicExponent value is invalid. return null(RSAPublicKey).");
            ex.printStackTrace();
        }
        if (modulus != null && publicExponent != null) {
            return generateRsaPublicKey(modulus, publicExponent);
        }
        return null;
    }

    /**
     * 根据给定的系数和专用指数构造一个RSA专用的公钥对象。
     *
     * @param modulus        系数。
     * @param publicExponent 专用指数。
     * @return RSA专用公钥对象。
     */
    public static RSAPublicKey generateRsaPublicKey(byte[] modulus, byte[] publicExponent) {
        RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(new BigInteger(modulus),
                new BigInteger(publicExponent));
        try {
            return (RSAPublicKey) keyFactory.generatePublic(publicKeySpec);
        } catch (InvalidKeySpecException ex) {
            System.out.println("RSAPublicKeySpec is unavailable.");
            ex.printStackTrace();
        } catch (NullPointerException ex) {
            System.out.println("RSAUtils#KEY_FACTORY is null, can not generate KeyFactory instance.");
            ex.printStackTrace();
        }
        return null;
    }

    /**
     * 根据给定的16进制系数和专用指数字符串构造一个RSA专用的私钥对象。
     *
     * @param hexModulus         系数。
     * @param hexPrivateExponent 专用指数。
     * @return RSA专用私钥对象。
     */
    public static RSAPrivateKey getRsaPrivateKey(String hexModulus, String hexPrivateExponent) {
        if (isBlank(hexModulus) || isBlank(hexPrivateExponent)) {
            System.out.println("hexModulus and hexPrivateExponent cannot be empty. RSAPrivateKey value is null to return.");
            return null;
        }
        byte[] modulus = null;
        byte[] privateExponent = null;
        try {
            modulus = HexUtil.hex2Bytes(hexModulus);
            privateExponent = HexUtil.hex2Bytes(hexPrivateExponent);
        } catch (Exception ex) {
            System.out.println("hexModulus or hexPrivateExponent value is invalid. return null(RSAPrivateKey).");
            ex.printStackTrace();
        }
        if (modulus != null && privateExponent != null) {
            return generateRsaPrivateKey(modulus, privateExponent);
        }
        return null;
    }

    /**
     * 根据给定的系数和专用指数构造一个RSA专用的私钥对象。
     *
     * @param modulus         系数。
     * @param privateExponent 专用指数。
     * @return RSA专用私钥对象。
     */
    public static RSAPrivateKey generateRsaPrivateKey(byte[] modulus, byte[] privateExponent) {
        RSAPrivateKeySpec privateKeySpec = new RSAPrivateKeySpec(new BigInteger(modulus),
                new BigInteger(privateExponent));
        try {
            return (RSAPrivateKey) keyFactory.generatePrivate(privateKeySpec);
        } catch (InvalidKeySpecException ex) {
            System.out.println("RSAPrivateKeySpec is unavailable.");
            ex.printStackTrace();
        } catch (NullPointerException ex) {
            System.out.println("RSAUtils#KEY_FACTORY is null, can not generate KeyFactory instance.");
            ex.printStackTrace();
        }
        return null;
    }

    /**
     * 使用给定的公钥加密给定的字符串。
     *
     * @param plaintext 字符串。
     * @return 给定字符串的密文。
     */
    public String encryptString(String plaintext) {
        byte[] data = plaintext.getBytes();
        try {
            byte[] enData = encrypt(getPublicKey(rsAconfig.getPublicKey()), data);
            return new String(Base64.getEncoder().encode(enData));
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    /**
     * 使用指定的公钥加密数据。
     *
     * @param key  给定的公钥。
     * @param data 要加密的数据。
     * @return 加密后的数据。
     */

    public static byte[] encrypt(Key key, byte[] data) throws Exception {
        Cipher ci = Cipher.getInstance(ALGORITHM);
        ci.init(Cipher.ENCRYPT_MODE, key);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = ci.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
            } else {
                cache = ci.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        return encryptedData;
    }


    /**
     * 使用给定的公钥解密给定的字符串。
     *
     * @param key         给定的公钥
     * @param encrypttext 密文
     * @return 原文字符串。
     */
    public String decryptString(Key key, String encrypttext) {
        if (key == null || isBlank(encrypttext)) {
            return null;
        }
        try {
            byte[] enData = Base64.getDecoder().decode(encrypttext);
            byte[] data = decrypt(key, enData);
            return new String(data);
        } catch (Exception ex) {
            ex.printStackTrace();
            System.out.println(String.format("\"%s\" Decryption failed. Cause: %s", encrypttext, ex.getCause().getMessage()));
        }
        return null;
    }

    /**
     * 使用指定的公钥解密数据。
     *
     * @param key  指定的公钥
     * @param data 要解密的数据
     * @return 原数据
     * @throws Exception
     */
    public static byte[] decrypt(Key key, byte[] data) throws Exception {
        Cipher ci = Cipher.getInstance(ALGORITHM);
        ci.init(Cipher.DECRYPT_MODE, key);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache = ci.doFinal(data, offSet, MAX_DECRYPT_BLOCK);
            } else {
                cache = ci.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return decryptedData;
    }

    /**
     * 判断非空字符串
     *
     * @param cs 待判断的CharSequence序列
     * @return 是否非空
     */
    private static boolean isBlank(final CharSequence cs) {
        int strLen;
        if (cs == null || (strLen = cs.length()) == 0) {
            return true;
        }
        for (int i = 0; i < strLen; i++) {
            if (Character.isWhitespace(cs.charAt(i)) == false) {
                return false;
            }
        }
        return true;
    }


    public static PublicKey getPublicKey(String key) throws Exception {
        byte[] keyBytes;
        keyBytes = Base64.getDecoder().decode(key);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(keySpec);
        return publicKey;
    }

    public PrivateKey getPrivateKey(String key) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
        byte[] keyBytes;
        keyBytes = Base64.getDecoder().decode(key);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
        return privateKey;
    }

    /**
     * 验签
     *
     * @param srcData 数据库的
     * @param sign    页面传来的
     * @return
     * @throws Exception
     */
    public boolean verify(String srcData, String sign) throws Exception {

        String s = decryptRsa(srcData);
        String s1 = decryptRsa(sign);
        return s.equals(s1);
    }


    public void generate() throws NoSuchAlgorithmException {
        // 加密算法
        String algorithm = "RSA";
        //  创建密钥对生成器对象
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
        // 生成密钥对
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        // 生成私钥
        PrivateKey privateKey = keyPair.getPrivate();
        // 生成公钥
        PublicKey publicKey = keyPair.getPublic();
        // 获取私钥字节数组
        byte[] privateKeyEncoded = privateKey.getEncoded();
        // 获取公钥字节数组
        byte[] publicKeyEncoded = publicKey.getEncoded();
        // 对公私钥进行base64编码
        String privateKeyString = Base64.getEncoder().encodeToString(privateKeyEncoded);
        String publicKeyString = Base64.getEncoder().encodeToString(publicKeyEncoded);
        // 打印私钥
        System.out.println(privateKeyString);
        // 打印公钥
        System.out.println(publicKeyString);
    }

    /**
     * 解密数据
     *
     * @param encrypted : 密文
     * @return : 原文
     * @throws Exception
     */
    public String decryptRsa(String encrypted) throws Exception {
        // 创建加密对象
        // 参数表示加密算法
        Cipher cipher = Cipher.getInstance("RSA");
        // 私钥进行解密
        cipher.init(Cipher.DECRYPT_MODE, getPrivateKey(rsAconfig.getPrivateKey()));
        // 由于密文进行了Base64编码, 在这里需要进行解码
        byte[] decode = Base64.getDecoder().decode(encrypted);
        // 对密文进行解密，不需要使用base64，因为原文不会乱码
        byte[] result = cipher.doFinal(decode);
        System.out.println(new String(result));
        return new String(result);

    }

    public void setRsAconfig(Rsaconfig rsAconfig) {
        this.rsAconfig = rsAconfig;
    }

    public static void main(String[] args) throws Exception {
        RsaUtil rsaUtil = new RsaUtil();

        Rsaconfig rsaconfig = new Rsaconfig();
        rsaconfig.setAlgorithm(RsaAlgorithm.DEFAULT);
        rsaconfig.setPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvrvh/BoZ6bcnCltEWSyT9WnDOlfwXOHWotOS3j1rgE50UjQ82CFKArvVj5qpTT2AJAlDYz0EzTDNXKluyPcOzxL7qJSdASjJuR5ewrXJcL/6Q51465vQqijlkYrZx/kg57Oy5eVSJKZhTpNiwAM0iJ7Mjt7gzPU4nqQCkzb8ZLvl8O/+93hrMQu4zk+QWjeBz6rqWNPZA4prOedUUUQIlsUpClL8+HuF6lhc4GiyXKIuqFv7yfKECZfdfI8+e5Awxc57sRqyTOMtQD34P5t6anYQhyTuM5CVHW/Z8q7o0MDeJ941MxmxUxlCASFbZPAfqOMBNI5UjjKs5uiB0CIvXQIDAQAB");
        rsaconfig.setPrivateKey("MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC+u+H8GhnptycKW0RZLJP1acM6V/Bc4dai05LePWuATnRSNDzYIUoCu9WPmqlNPYAkCUNjPQTNMM1cqW7I9w7PEvuolJ0BKMm5Hl7Ctclwv/pDnXjrm9CqKOWRitnH+SDns7Ll5VIkpmFOk2LAAzSInsyO3uDM9TiepAKTNvxku+Xw7/73eGsxC7jOT5BaN4HPqupY09kDims551RRRAiWxSkKUvz4e4XqWFzgaLJcoi6oW/vJ8oQJl918jz57kDDFznuxGrJM4y1APfg/m3pqdhCHJO4zkJUdb9nyrujQwN4n3jUzGbFTGUIBIVtk8B+o4wE0jlSOMqzm6IHQIi9dAgMBAAECggEBAL6LdHgmmzyhfJ8cDmwH3MiPoDnpIHeqJoMEWvUq5vqHZi25gg9pMkcI35FXMUu45QsbNfcuPl2/I2ou2j5qkqDMlh0UWsWNufj2HjTm5xd8sshZyTOXth5IPQ/pZygf+n9/RnnlXuq+TPfyxfSNPy49dp5EitA3KxlRQEFR7VOzqJPSqAivL4tTcGZ6hCeHTT5u9WJXBENWm0VGuVpV1iIfHtQ1pio8k8XqPO29E5JzEbgv6tqI4E9jT1Nu2WlqvpwuG3BEG9A/O2mZPEENDaCwiiTY+PlqFd7RD2d4aSXgLJoqJc86MaJmR7Q2CIujb1m8z6vqvNp5U6+nKnfVOQECgYEA7bcBCGwrClMbhgU65gKWZKVRyThhdBxWCeA+dHvUOeR5BY+1P6Rw3i7BvVSqOvNCx+AiHKoCZVVvhA1EBXNX2qaCzL0y3AZROKeOVfqlCIcSHmlQCYk9tCsguW5FW46IsQjrqo5COnpP3ffSD9lDN48FmEHowY/QGwVCa0fd6HcCgYEAzWe/Lr4C1gC16HyWoDMMgz4h3369kRflrxE0xC12n+6l+PypxfZQey31NFj3xG0ZsS+SEiI89xH8L64cwJHWlahBNLb5DoBFVcgm2jiqEB00O1rBAd89DDQ4rK7sXVf0Sau+/chAUDKH6ZAzR8kQRfQn0iQ3jrga0cik/CKrL8sCgYBbM7M5wAZjV69fo/IM/UEmLCdMxEzfd1+rOlW76yZpb7be41vupvCGzfmW4jzNkTc16FAuHyv8JOM8DrriPHagdPIKCyzQLbxLuD1rz1YyB2B0OoNypSz/SuyrOBt0HSNkiL+iGCJgvTyXNNVrLzgSwNSsrdzXJo35T/NDpbcF0QKBgHT5YizyVJcerHmLihQNPlD3bz7h5H7ehxTdzZjy8x09QRO8f8qW7VpY381B5UfdgcYf+yWMjf9+JInas9EOvI9Z1Fh64vbZIaMqasXXgzHOD9LFaijwwslSa/wbWcwGrL3PquLHyahiPnhKgIme/T95Pbw0l9cVDWDuzNYxwB9lAoGAYF4TSeEjbnQKoo5/H3bsTOdr0c27KIyBKrpNfXosy01l2f5kLdFmDDGvIgvVXtN3Tt3Jsi+NWb6zaPgEWGpbGqVvtPnDZVpnTB8kV/fTw5puUbg6JgRn2jUix2yyRj5hcceuv6kvmz0SNElmWxucbIUD3fcoItrWBjD2BM5z3qI=");
        rsaUtil.rsAconfig = rsaconfig;

        String s = rsaUtil.encryptString("Zpepc001@");
        System.out.println(" 加密后： " + s);
        String s3 = rsaUtil.decryptRsa(s);
        System.out.println(" 解密后： " + s3);
        String s4 = "hnOOKEi0b8YSRVtPyCEsdyWw6kE8P/jntaCDgmhPrbE63XHvZXc0PNYCXK78pJG9Xol+k1z+bFFMStD9VbhNa2l86rYJnM2txI/HjmR3GPROoSW/tlEV3gokCVujcyJ9UFyDqjcbJ6MWbBjBxnhJxKoOGt8tYzaUkFERGlw9SH390ugPBPuwFM+Y9PcefWaE1OZkv1iwGm+DORSy62epL0uZB3pQlAo9iME0vNqDHhqeBpDnOm3I+YJzRCZF7s10l0x73FedlIvbQzNCk2xnT3t5rt4wx3c3THTLEWf8u9O4wud7ZMSg4fBDFqjxehAihbdIxda0GENgAGVlT3uI3Q==";
        String s5 = rsaUtil.decryptRsa(s4);
        System.out.println(" 解密后： " + s5);
    }
}
